#include <stdio.h> 
#include <errno.h> 
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> 
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <libgen.h>
#include <poll.h>

#define ARRAY_SIZE(x)   (sizeof(x)/sizeof(x[0]))        //判断有多少个元素

static inline void msleep(unsigned long ms);
static inline void print_usage(char * progname);
int socket_server_init(char *listen_ip,int listen_port);

static inline void print_usage(char *progname)
{
      printf("%s usage: \n",progname);
      printf("-d(daemon)set programe running on back ground\n");
      printf("-p(--port):sepcify server listen port.\n");
      printf("-h(--help):printf this help information.\n");

      return ;
}



int main(int argc,char **argv)
{
    int                     rv=-1;
    int                     max;
    int                     port=0;
    int                     ch;
    int                     opt; 
    int                     listenfd,connfd;
    int                     daemon_run=0;
    char                   *progname= NULL;
    fd_set                  rdset;
    int                     i,j;
    int                     found;
    int                     maxfd=0;
    char                    buf[1024];
    struct pollfd           fds_array[1024];
    struct option           opts[]=
    {
            {"daemon",no_argument,NULL,'b'},
	    {"port",required_argument,NULL,'p'},
            {"help",no_argument,NULL,'h'},
	    {NULL,0,NULL,0}                             
    };

    progname = basename(argv[0]);

    
    while ( (ch=getopt_long(argc,argv,"bp:h",opts,NULL)) != -1)
    {
         
	  switch (ch)
	  {
                  case 'b':
			  daemon_run=1;
			  break;


                  case 'p':
                          port=atoi(optarg);
                          break;

                  case 'h':
                          print_usage(argv[0]);
                          return 0;			  
	  }  
    }
    
    if( !port)
    {
	    print_usage(argv[0]);
	    return 0;
     
    }
 
    if(listenfd=socket_server_init(NULL,port)<0)      //监听listen
    {
	    printf("ERROR: %s server listen on port %d failure\n",argv[0],port);
	    return -1;
    
    }   
    printf("%s server start to listen on port %d successful\n",argv[0],port);

    if( daemon_run)                              //是否转到后台
    {
	    daemon(0,0);
    }

    for(i=0; i<ARRAY_SIZE(fds_array); i++)
    {
	    fds_array[i].fd = -1;              //初始化poll的文件描述符

    }
    fds_array[0].fd = listenfd;               //监听的描述符
    fds_array[0].events = POLLIN;             //普通或优先级带数据可读

    max = 0;

    while (1)
    {
	    rv = poll(fds_array,max+1, -1);
	    if(rv<0)
	    {
		    printf("select failure: %s\n",strerror(errno));
		    break;
	    }
	    else if(rv == 0)
	    {
		    printf("select get timeout\n");
		    continue;
	    }



	    //监测listenfd(监听套接字)是否存在连接
	    if( fds_array[0].revents &POLLIN)
	    {
		    if((connfd = accept(listenfd,(struct sockaddr *)NULL,NULL))<0)
		    {
			    printf("accept new client failure :%s \n",strerror(errno));
			    continue;
		    }

		    found = 0;

		    //将提取到的connfd放入poll结构体数组中，以便于poll函数监测
		    for(i=0;i<ARRAY_SIZE(fds_array); i++)
		    {
                            if( fds_array[i].fd < 0 ) 
			    {
				     printf("accept new client[%d] \n", connfd );
				     fds_array[i].fd = connfd; 
				     fds_array[i].events = POLLIN;
				     found = 1; 
				     break;
			    }
		    }

                            if( !found )      //说明客户端数量过多已满
			    {
				    printf("accept new client[%d] but full ,so refuse it \n",connfd);
				    close (connfd);
			    }




		    //max更新
		    max = i>max ? i:max;

		    // 如果没有就绪的描述符，就继续poll监测，否则继续向下看
		    if(--rv<=0)
			    continue;
	    }

	    else
	    {
		     for(i=1; i<ARRAY_SIZE(fds_array); i++)           // 遍历整个数组，继续响应就绪的描述符
		     {
			      if( fds_array[i].fd < 0 )
			      {
				       continue;
			      }



                              // 接收数据
			       if( (rv=read(fds_array[i].fd, buf, sizeof(buf))) <= 0)
			       {
				        printf("socket[%d] read failure or get disconncet.\n", fds_array[i].fd); 
					close(fds_array[i].fd); 
					fds_array[i].fd = -1;
			       }
			       else
			       {
				       printf("socket[%d] read get %d bytes data\n", fds_array[i].fd, rv);
				        
				       for(j=0; j<rv; j++)
					        buf[j]=toupper(buf[j]);

				       if( write(fds_array[i].fd, buf, rv) < 0 ) 
				       {
					        printf("socket[%d] write failure: %s\n", fds_array[i].fd, strerror(errno)); 
						close(fds_array[i].fd); 
						fds_array[i].fd = -1;
				       }
			       }
		     }
	    }

    }


    close(listenfd);
    return 0;
 
}

int socket_server_init(char *listen_ip, int listen_port) 
{
         struct sockaddr_in      serv_addr;
	 int                     rv = 0;
	 int                     on = 1;
	 int                     listenfd;

if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
	printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno)); 
	return -1;
}
    
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(listen_port);
    //inet_aton( "", &serv_addr.sin_addr );
  
    if( !listen_ip)
    {
	    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	
    }
    else 
    {
	    if (inet_pton(AF_INET, listen_ip, &serv_addr.sin_addr) <= 0)  
	    {
		     printf("inet_pton() set listen IP address failure.\n"); 
		     rv = -2;
		     goto cleanup;
	    }
    }

    if(bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0);  
    {
           printf("use bind failure: %s\n", strerror(errno)); 
	   rv = -3;
	   goto cleanup;
           
    } 
    
    if(listen(listenfd,13)<0);
    {
           printf("listen on portr[%d] failure\n",strerror(errno));
	   rv = -4;
	   goto cleanup;
    }

cleanup:
    if(rv<0)
	close(listenfd);
    else
        rv = listenfd;
    
    return rv;
 }
	    

